#include "CObjectManager.h"
#include <assert.h>
#include "../core/CDirect3D.h"
#include "../core/CCamera.h"
#include "../gameobjects/CPlayerObject.h"
#include "../gameobjects/CBaseEnemy.h"
#include "../gameobjects/CEnemySpawner.h"
#include "../gameobjects/CProjectile.h"
/////////////////////////
#define NULL 0

CObjectManager* CObjectManager::m_pInstance = new CObjectManager();

CObjectManager::CObjectManager(void)
{
	m_pCamera = NULL;
}


CObjectManager::~CObjectManager(void)
{
}

CObjectManager* CObjectManager::GetInstance()
{
	return m_pInstance;
}
void CObjectManager::FreeInstance()
{
	assert(m_pInstance != NULL && "Instance already free!");
	delete m_pInstance;
}


void CObjectManager::Initialize(CCamera *pCamera)
{
	m_pCamera = pCamera;
}

void CObjectManager::Shutdown()
{
	FlushObjects();
}

void CObjectManager::AddObject(IBaseObject* pObject)
{
	m_ActiveObjectList.push_back(pObject);
}

void CObjectManager::RemoveObject(IBaseObject* pObject)
{
	m_ObjectsToRemove.push_back(pObject);
}

void CObjectManager::FlushPendingObjects()
{
	if(m_ObjectsToRemove.size() == 0)
	{
		return;
	}
	std::list<IBaseObject*>::iterator objectsToRemoveIterator = m_ObjectsToRemove.begin();
	for(; objectsToRemoveIterator != m_ObjectsToRemove.end(); ++objectsToRemoveIterator)
	{
		IBaseObject* pRemove = objectsToRemoveIterator._Ptr->_Myval;
		pRemove->Destroy();
		m_ActiveObjectList.remove(pRemove);
		m_FreeObjectList.push_back(pRemove);
	}
	m_ObjectsToRemove.clear();
}

void CObjectManager::FlushObjects()
{
	// remove any pending objects
	FlushPendingObjects();

	// remove any free objects
	while(m_FreeObjectList.size() > 0)
	{
		IBaseObject* pRemove = (*m_FreeObjectList.begin());
		pRemove->Destroy();
		m_FreeObjectList.remove(pRemove);
		delete pRemove;
	}

	// remove any active objects
	while(m_ActiveObjectList.size() > 0)
	{
		IBaseObject* pRemove = (*m_ActiveObjectList.begin());
		pRemove->Destroy();
		m_ActiveObjectList.remove(pRemove);
		delete pRemove;
	}


}


CPlayerObject* CObjectManager::CreatePlayerObject(float posX, float posY)
{
	m_pPlayer = (CPlayerObject*)FindFreeObjectByType(OBJECT_PLAYER);
	if(m_pPlayer == NULL)
	{
		m_pPlayer = new CPlayerObject();
	}
	m_pPlayer->Initialize(posX, posY, 0.0f, 16, 16);
	m_ActiveObjectList.push_back((IBaseObject*)m_pPlayer);
	return m_pPlayer;
}

CBaseEnemy*	CObjectManager::CreateEnemy(float posX, float posY, EnemyType enemyType)
{
	CBaseEnemy* pEnemy = (CBaseEnemy*)FindFreeObjectByType(OBJECT_ENEMY);

	// make sure the enemy is allocated
	if(pEnemy == NULL)
	{
		switch(enemyType)
		{
		case ENEMY_BASE:
			{
				pEnemy = new CBaseEnemy();
			} break;
		case ENEMY_SPAWNER:
			{
				pEnemy = new CEnemySpawner();
			} break;
		default:
			{
				assert(false && "Invalid Enemy Type!");
			}
		};
	}

	// initialize
	switch(enemyType)
	{
	case ENEMY_BASE:
		{
			pEnemy->Initialize(posX, posY, 0.0f, 32, 32, m_pPlayer);
		} break;
	case ENEMY_SPAWNER:
		{
			((CEnemySpawner*)pEnemy)->Initialize(posX, posY, 0.0f, 48, 48, ENEMY_BASE, 200.0f, 0.01f, 50, m_pCamera->GetTargetObject());
		} break;
	default:
		{
			assert(false && "Invalid Enemy Type!");
		}
	};

	m_ActiveObjectList.push_front((IBaseObject*)pEnemy);
	return pEnemy;
}

CProjectile* CObjectManager::CreateProjectile(float posX, float posY, float dirX, float dirY)
{
	CProjectile* pProjectile = (CProjectile*)FindFreeObjectByType(OBJECT_PROJECTILE);
	if(pProjectile == NULL)
	{
		pProjectile = new CProjectile();
	}
	pProjectile->Initialize(posX, posY, 0.0f, dirX, dirY, 10);
	m_ActiveObjectList.push_back((IBaseObject*)pProjectile);
	return pProjectile;
}

IBaseObject* CObjectManager::FindFreeObjectByType(ObjectType type, int subType)
{
	IBaseObject* returnObj = NULL;

	std::list<IBaseObject*>::iterator FreeObjIterator = m_FreeObjectList.begin();
	for(; FreeObjIterator != m_FreeObjectList.end(); ++FreeObjIterator)
	{
		// check type
		if((*FreeObjIterator)->Type() == type)
		{
			// check subtypes
			if(type == OBJECT_ENEMY)
			{
				if(((CBaseEnemy*)(*FreeObjIterator))->EType() != (EnemyType)subType)
				{
					continue;
				}
			}
			returnObj = (*FreeObjIterator);
			m_FreeObjectList.remove(returnObj);
			break;
		}
	}

	return returnObj;
}

void CObjectManager::Update(float fDeltaTime)
{
	// update all objects
	std::list<IBaseObject*>::iterator ActiveIterator = m_ActiveObjectList.begin();
	for(; ActiveIterator != m_ActiveObjectList.end(); ++ActiveIterator)
	{
		(*ActiveIterator)->Update(fDeltaTime);
	}

	// check collisions
	std::list<IBaseObject*>::iterator outerIterator = m_ActiveObjectList.begin();
	for(; outerIterator != m_ActiveObjectList.end(); ++outerIterator)
	{
		std::list<IBaseObject*>::iterator innerIterator = m_ActiveObjectList.begin();
		for(; innerIterator != m_ActiveObjectList.end(); ++innerIterator)
		{
			// dont check agains the same object
			if((*innerIterator) != (*outerIterator))
			{
				(*outerIterator)->CheckCollision((*innerIterator));
			}			
		}
	}


	FlushPendingObjects();
}

void CObjectManager::Input(CRetroInput* pInput)
{
	std::list<IBaseObject*>::iterator ActiveIterator = m_ActiveObjectList.begin();
	for(; ActiveIterator != m_ActiveObjectList.end(); ++ActiveIterator)
	{
		(*ActiveIterator)->Input(pInput);
	}
}

void CObjectManager::Render(CDirect3D* pRender)
{
	std::list<IBaseObject*>::iterator ActiveIterator = m_ActiveObjectList.begin();
	for(; ActiveIterator != m_ActiveObjectList.end(); ++ActiveIterator)
	{
		(*ActiveIterator)->Render(pRender, m_pCamera);
	}
}
